--- id: TASK-002 title: 'User config: select & order leagues' status: "\U0001F3C1 Done" assignee: - '@humdrum-tiv' created_date: '2026-06-16 18:03' updated_date: '2026-06-18 01:17' labels: - feature dependencies: [] priority: high ordinal: 2000 --- ## Description Let users choose which leagues appear and in what order, instead of the hardcoded model.Leagues set. Persisted config (see config-persistence task). ## Acceptance Criteria - [x] #1 user can enable/disable leagues - [x] #2 league order is user-configurable - [x] #3 choice persists across runs ## Implementation Plan 1. config.Leagues = ordered ENABLED league IDs; empty = default (all, default order). 2. App holds effective `leagues []model.League` built by resolveLeagues(cfg.Leagues); replace all model.Leagues reads in ui (sections/views/ticker/commands/update/app) with a.leagues + a.leagueIndex(). 3. fetchAll takes the league slice so only enabled leagues are polled. 4. New viewSettings mode: list all leagues (enabled then hidden), space toggles, K/J (shift+up/down) reorder enabled, esc closes+persists+refetches. Open with 'L'. 5. On close: drop filter if its league got disabled; clampCursor. 6. Order drives live/recent/upcoming + tabs automatically (already keyed off league order). 7. Tests: resolveLeagues, toggle, reorder, sort respects custom order. ## Final Summary Users can now select, hide, and reorder leagues; choice persists. What changed: - App holds an effective league set (App.leagues) built by resolveLeagues(config.Leagues). All view/fetch/sort paths (sections, tabs, ticker, fetchAll, leagueIndex) now iterate a.leagues instead of the hardcoded model.Leagues catalog. - config.Leagues persists the ordered ENABLED league IDs; empty/nil (or all-unknown) means default = all leagues in catalog order. Disabling drops a league from views (cached games retained); enabling appends to the end. Last-enabled league can't be disabled. - New settings screen (viewSettings / settings.go, opened with 'L'): lists enabled then hidden leagues; space toggles, K/J (shift+up/down) reorder the enabled group, esc closes -> persists, drops a now-disabled active filter, and refetches. - League order is the single source of truth for both tab order and the live/recent/upcoming sort, so reordering NBA above World Cup makes NBA lead those views whenever it has games for the date. Tests: resolveLeagues (default/subset+order/all-unknown fallback), setLeague toggle, moveLeague reorder+clamp, and a state-filter sort honoring custom league order. Existing all-leagues/ticker test fixtures updated to set leagues. go vet + go test ./... pass.